-- The MIT License
-- 
-- Copyright (c) 2008 Matthew Maravillas, Patrick Quinn
-- 
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
-- 
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
-- 
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-- THE SOFTWARE.

function zip(table1, table2)
    local firstPairs = ipairs(table1)
    local secondPairs = ipairs(table2)
    
    local i = 0
    
    return function() 
        local index1, value1 = firstPairs(table1, i)
        local index2, value2 = secondPairs(table2, i)
        
        i = i + 1
        
        return value1, value2
    end
end

function areTablesEqual(table1, table2)
    if table.getn(table1) != table.getn(table2) then
        return false
    end
    
    local comp = function(val1, val2) 
        return val1 < val2 
    end
    
    table.sort(table1, comp)
    table.sort(table2, comp)

    for value1, value2 in zip(table1, table2) do
        if value1 != value2 then
            return false
        end
    end
    
    return true
 end

function arrayToDict(array)
    -- Transforms an array like {'foo', 'bar'} into a dict like {foo=true, bar=true}
    local dict = {}
    for i, v in array do
        dict[v] = true
    end
    return dict
end

function dictToArray(dict)
    local array = {}
    for k, v in dict do
        table.insert(array, k)
    end
    return array
end

function arrayDifference(arr1, arr2)
    -- returns three values, the items that are present in arr1 but not in arr2, the
    -- items present in arr2 but not arr1, and the items present in both lists
    dict1 = arrayToDict(arr1)
    dict2 = arrayToDict(arr2)
    missingFrom1 = {} 
    missingFrom2 = {} 
    intersection = {}
   
    for k, v in dict1 do
        if dict2[k] then
            table.insert(intersection, k)
        else
            table.insert(missingFrom2, k)
        end
    end

    for k, v in dict2 do
        if not dict1[k] then
            table.insert(missingFrom1, k)
        end
    end
   
    return missingFrom2, missingFrom1, intersection
end

CreateEvent = function(class, eventName)
    -- Create the list of event callback lists if it doesn't exist
    if not class.Callbacks then
        class.Callbacks = {}
    end
    
    local callback = 'On' .. eventName
    
    -- Create the list of callbacks in class.Callbacks.OnEventHappened
    class.Callbacks[callback] = {}
    
    -- Create the class.EventHappened function
    class[eventName] = function(self, ...)
        if class.Callbacks[callback] then
            for index, func in class.Callbacks[callback] do
                if func then
                    func(unpack(arg))
                end
            end
        end       
    end
        
    -- Create the add event handler function, AddEventHappenedCallback
    local addCallback = 'Add' .. eventName .. 'Callback'
    class[addCallback] = function(self, func)
        if not func then
            error(addCallback .. ': Tried to add a callback with a nil function')
            return
        end
        
        table.insert(class.Callbacks[callback], func)
    end
end